/*
 * PPMC.cpp
 *
 *  Created on: 07/06/2010
 *      Author: administrador
 */

#include "PPMC.h"
#include "ContextoMenos1.h"
#include <stdio.h>

string const PPMC::claveContextoOrdenCero = "\b";

PPMC::PPMC(int ordenMaximo, EstrategiaPPMC* estrategia)
{
	this -> ordenMaximo = ordenMaximo;

	/* Inicializo la estrategia del PPMC y la clave del contexto de orden 0 */
	this -> estrategia = estrategia;

	this->inicializarVariables();

}

PPMC::~PPMC()
{
	delete this -> estrategia;
}

unsigned int PPMC::getOrdenMaximo()
{
	return this -> ordenMaximo;
}

string PPMC::obtenerClaveContexto(int ordenAEncontrar){

	string claveContexto;

	if (ordenAEncontrar == 0)
		{
			claveContexto = PPMC::claveContextoOrdenCero;
		}
		else
		{
			int maximoCaracter = 0;

			if (this->ordenMaximo < this->cantidadCaracteresLeidos){

				 maximoCaracter = this->ordenMaximo ;
			}
			else{

				maximoCaracter = this->cantidadCaracteresLeidos;
			}

			int i = maximoCaracter - ordenAEncontrar;
			while (i  < maximoCaracter)
			{
				claveContexto += this->caracteresLeidos[i];
				i++;
			}
		}

	return claveContexto;
}

void PPMC::inicializarVariables(){

	this->caracterActual = 0;

	this->CtxActual = NULL;

	this->CtxAnterior = NULL;

	this -> cantidadCaracteresLeidos = 0;

	this->ordenActual = 0;

	this->caracteresLeidos = new typeCaracterChar[this->ordenMaximo+1];

	this->caracteresLeidos[0] = '\0';

	/* Agrego el contexto de orden 0 */

	Contexto* contextoInicial = new Contexto(claveContextoOrdenCero);

	this -> estrategia -> agregarContexto(contextoInicial);

}


int PPMC::obtenerOrdenActual()
{
	if (this -> ordenMaximo < this -> cantidadCaracteresLeidos)
		return this -> ordenMaximo;
	else
		return this -> cantidadCaracteresLeidos;
}

Contexto* PPMC::obtenerContexto()
{

	if (this->ordenActual<0){

		return new ContextoMenos1();
	}

	string claveContexto = this->obtenerClaveContexto(this->ordenActual);

	Contexto *buscado = this -> estrategia -> buscarContexto(claveContexto);

	if (!buscado){

		buscado = new Contexto(claveContexto);

		this->estrategia->agregarContexto(buscado);

	}	
	
	return buscado ;
}

void PPMC::agregarContexto()
{
	string claveContexto;

	unsigned int i;

	if (this -> ordenMaximo < this -> cantidadCaracteresLeidos)
	{
		for (i = this -> ordenMaximo - this->ordenActual; i < this -> ordenMaximo; i++)
			claveContexto += this->caracteresLeidos[i];
	}
	else
	{
		for (i = this -> cantidadCaracteresLeidos - this->ordenActual; i < this -> cantidadCaracteresLeidos; i++)
			claveContexto += this->caracteresLeidos[i];
	}

	claveContexto += this->caracterActual;



}

//char PPMC::interpretarCaracter(unsigned int caracterNumero)
//{
//	//todo implementar
//	return 'a';
//}

string PPMC::buscarClaveAnterior(string claveActual)
{
	string claveAnterior;

	claveAnterior = claveActual.substr(1,claveActual.size());

	return claveAnterior;
}

string PPMC::buscarClaveSiguiente(string claveActual)
{
//	string claveAnterior;
//	int desde;
//
//	if(claveActual == PPMC::claveContextoOrdenCero)
//	{
//		desde = CtxMaximo->getClave().size() - 1;
//
//	}else
//	{
//		desde = CtxMaximo->getClave().size() - claveActual.size() - 1 ;
//	}
//
//	claveAnterior = CtxMaximo -> getClave().substr(desde, CtxMaximo->getClave().size());
//
//	return claveAnterior;
}

Contexto* PPMC::getContextoAnterior()
{

//	string claveActual = CtxActual -> getClave();
//
//	if(CtxActual -> getOrden() == 0)
//	{
//		//TODO: deberia retornar el -1
//	}else
//	{
//		if(CtxActual -> getOrden() == 1)
//		{
//
//			return this->estrategia->buscarContexto(PPMC::claveContextoOrdenCero);
//
//		}else
//		{
//			return this -> estrategia -> buscarContexto(buscarClaveAnterior(claveActual));
//		}
//	}

	return NULL;
}

Contexto* PPMC::getContextoSiguiente()
{
	string claveActual = CtxActual -> getClave();

	if(CtxActual -> getOrden() == -1)
	{
		return this->estrategia->buscarContexto(PPMC::claveContextoOrdenCero);

	}else
	{
		return this -> estrategia -> buscarContexto(buscarClaveSiguiente(claveActual));
	}

	return NULL;
}

void PPMC::agregarCaracterLeido()
{
	if (this -> ordenMaximo <= this -> cantidadCaracteresLeidos)
	{
		for (unsigned int i = 1; i < this -> ordenMaximo; i++)
			this->caracteresLeidos[i-1] = this->caracteresLeidos[i];

		this->caracteresLeidos[this -> ordenMaximo - 1] = this->caracterActual;
		this->caracteresLeidos[this->ordenMaximo]='\0';
	}
	else{
		this->caracteresLeidos[this->cantidadCaracteresLeidos] = (char)this->caracterActual;
		this->caracteresLeidos[this->cantidadCaracteresLeidos+1] = '\0';
	}

}

void PPMC::comprimir(char* rutaArchivo)
{
	FILE* archivo;

	archivo = fopen(rutaArchivo, "r");

	/* Verifico que el archivo sea valido */
	if(archivo == NULL)
	{
		throw ArchivoInvalidoException(rutaArchivo);
	}

	bool emitidoESCAPE;

	bool fin = false;

	/* variable utilizada para saber cuantos caracteres se restan en la exclusion */
//	int exclusion = 0;

	this->caracterActual = fgetc(archivo);

	cout <<"\n------------Comienza la compresion - Orden Máximo "<< this->ordenMaximo <<" --------------------------\n\n";

	while (!fin)
	{
		cout << "---------------------Comprimiendo el caracter \"" << caracterActual << "\" -- Caracteres Leidos \""<< caracteresLeidos << "\" Cantidad leidos ---------------------" <<"\n\n";

		this->ordenActual = this ->obtenerOrdenActual();

		emitidoESCAPE = true;

		while ((this->ordenActual >= -1) && (emitidoESCAPE))
		{
			this->CtxActual = this -> obtenerContexto();

			cout << "Orden Actual: "<< ordenActual <<"\n";

			emitidoESCAPE = this->CtxActual -> emitir(this->caracterActual);

			if ((emitidoESCAPE))
			{
				this->CtxActual->agregarDistribucion(this->caracterActual);
			}

			if (this->ordenActual>=0){
				this->estrategia->actualizarContexto(this->CtxActual);
			}

			delete this->CtxActual;

			this->ordenActual--;

		}
			cout << "**********************************************************\n\n";

//		if (emitidoESCAPE)
//		{
//			this -> emitir(caracterActual, exclusion);
//
//			exclusion++;
//		}

		this -> agregarCaracterLeido();

		this -> cantidadCaracteresLeidos++;

		if (this->caracterActual==(typeCaracterAscii)EOF){

			fin = true;
		}

		this->caracterActual = fgetc(archivo);

	}

	fclose(archivo);
}

void PPMC::descomprimir(char* rutaArchivo){

	this->inicializarVariables();

	FILE* archivo;

	archivo = fopen("archivoDescomprimido", "w+");

	/* Verifico que el archivo sea valido */

	if(archivo == NULL)
	{

		throw ArchivoInvalidoException(rutaArchivo);
	}

	bool emitidoESCAPE;
cout << "comienza Descompresion" <<'\n';
	typeCaracterAscii caracterDescomprimido = Compresor::descomprimir();

	while (this->caracterActual !=typeCaracterAscii(EOF)){

	cout << "caracterDescomprimido "<< caracterDescomprimido << '\n';

	emitidoESCAPE = true;

	this->ordenActual = this->obtenerOrdenActual();

		while (this->ordenActual>=-1 && emitidoESCAPE ){

			this->caracterActual = this->actualizarDescompresion(caracterDescomprimido);

				cout << "Caracter Emitido " << this->caracterActual <<'\n';

			if (this->caracterActual!=Distribucion::CARACTER_ESCAPE && this->caracterActual!=(typeCaracterAscii)EOF){

				typeCaracterChar Caracter = (typeCaracterChar)this->caracterActual;


				fwrite((void*)&Caracter,sizeof(char),sizeof(char),archivo);

				emitidoESCAPE = false;
			}

			this->ordenActual--;

			caracterDescomprimido = Compresor::descomprimir();

		}

		this -> agregarCaracterLeido();

		this -> cantidadCaracteresLeidos++;

		cout <<"Caracteres Leidos " << this->caracteresLeidos <<'\n';

	}

	fclose(archivo);

	//TODO: Falta implementar;

}

void PPMC::actualizarContextosAnteriores(typeCaracterChar caracter){

	int ordenMaximo = this->obtenerOrdenActual();

	string claveContexto;

	for (int i=ordenMaximo; i>this->ordenActual; i--){

		claveContexto = this->obtenerClaveContexto(i);
		Contexto *contextoBuscado = this->estrategia->buscarContexto(claveContexto);
		contextoBuscado->agregarDistribucion((char)caracter);
		this->estrategia->actualizarContexto(contextoBuscado);

	}

}

typeCaracterAscii PPMC::actualizarDescompresion(typeCaracterAscii caracterNumero)
{

	this->CtxActual = this->obtenerContexto();

	cout << this->CtxActual->getClave() << '\n';

	typeCaracterAscii caracter = this->CtxActual->emitirDescomprimido(caracterNumero);

	if (caracter !=Distribucion::CARACTER_ESCAPE){

		this->actualizarContextosAnteriores(caracter);
	}

	this->estrategia->actualizarContexto(this->CtxActual);

	delete this->CtxActual;

	this->CtxActual=NULL;

	return caracter;

	//se ve a que caracter corresponde dentro del CtxActual
//	aEmitir = interpretarCaracter(caracterNumero);

//	if(aEmitir == Distribucion::CARACTER_ESCAPE)
//	{
//		//Se actualiza el CtxActual agregando el ESC. Si es el -1 no hace nada
//		CtxActual -> acumularCaracter(aEmitir);
//
//		//Se actualiza la lista de Exclusion, se agregan los caracteres que no esten en la lista y que no sean ESC
//		//actualizarExclusion(CtxActual);
//
//		//Si CtxActual = 0 -> CtxAnterior = -1. Si CtxActual = 'xyz' -> CtxAnterior = 'yz'
//		CtxActual = getContextoAnterior();
//
//		return Distribucion::CARACTER_ESCAPE;
//		//no se deberia escribir nada en el archivo
//
//	}else{
//		//Encontre el caracter que queria
//		while(CtxActual -> getOrden() <= CtxMaximo -> getOrden())
//		{
//
//			if(CtxActual -> getOrden() != -1)
//			{
//
//				//Se actualiza el CtxActual agregando el caracter. Si es el -1 no hace nada
//				CtxActual -> acumularCaracter(aEmitir);
//
//				if(CtxActual -> getOrden() != 0)
//				{
//					claveAux = aEmitir + CtxActual -> getClave();
//				}else
//				{
//					claveAux = aEmitir;
//					//pues la clave del contexto 0 es vacia
//				}
//
//				//Creo que para Ctx 0 quedaria solo claveAux = aEmitir
//				if((int)claveAux.size() <= ordenMaximo)
//				{
//					CtxAux = new Contexto(claveAux);
//					//agregarContexto(CtxAux);
//					//solo en caso de que no exista
//				}
//
//			}
//
//
//			//Esto se pregunta porque cuando obtenga el siguiente me aseguro que haya siguiente y no sigo iterando sin salida
//			if(CtxActual -> getClave() != CtxMaximo -> getClave())
//			{
//				CtxActual = getContextoSiguiente();
//				//al reves que el anterior, lo obtiene usando el actual y el	maximo
//			}else{
//				CtxActual = CtxAux;
//			}
//		}
//
//		//Finalizo y dejo listo para la proxima letra
//		CtxActual = CtxAux;
//		CtxMaximo = CtxActual;
//		//listaExclusion -> limpiar();
//		return aEmitir;
//	}

}

